package cn.kennylee.codehub.springmvc.appfilters.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.URLDecoder;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * URL解码过滤器
 * <p>Created on 2025/7/24.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Slf4j
public class UrlDecodeFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;

        HttpMethod httpMethod = HttpMethod.valueOf(httpRequest.getMethod().toUpperCase());

        // 只处理GET请求
        if (httpMethod == HttpMethod.GET) {
            request = new UrlDecodeRequestWrapper(httpRequest);
        }

        chain.doFilter(request, response);
    }

    private static class UrlDecodeRequestWrapper extends HttpServletRequestWrapper {

        private final Map<String, String[]> decodedParams;

        public UrlDecodeRequestWrapper(HttpServletRequest request) {
            super(request);
            this.decodedParams = decodeParameters(request);
        }

        @Override
        public String getParameter(String name) {
            String[] values = getParameterValues(name);
            return (values != null && values.length > 0) ? values[0] : null;
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            return Collections.unmodifiableMap(decodedParams);
        }

        @Override
        public Enumeration<String> getParameterNames() {
            return Collections.enumeration(decodedParams.keySet());
        }

        @Override
        public String[] getParameterValues(String name) {
            return decodedParams.get(name);
        }

        private Map<String, String[]> decodeParameters(HttpServletRequest request) {
            Map<String, String[]> originalParams = request.getParameterMap();
            Map<String, String[]> decodedParams = new HashMap<>(originalParams.size());

            boolean isModified = false;
            for (Map.Entry<String, String[]> entry : originalParams.entrySet()) {
                String key = entry.getKey();
                String[] values = entry.getValue();
                if (key == null || values == null || values.length == 0) {
                    continue;
                }

                // 解码参数名
                String decodedKey = URLDecoder.decode(key, StandardCharsets.UTF_8);

                // 解码每个参数值
                String[] decodedValues = new String[values.length];
                for (int i = 0; i < values.length; i++) {
                    String originValue = values[i];
                    decodedValues[i] = URLDecoder.decode(originValue, StandardCharsets.UTF_8);
                    if (!isModified && !originValue.equals(decodedValues[i])) {
                        // 如果有任何值被修改，标记为已修改
                        isModified = true;
                    }
                }

                decodedParams.put(decodedKey, decodedValues);
            }

            if (isModified) {
                // 如果有任何参数被修改，打印
                log.debug("URL解码参数值，原始参数: {}, \n解码后参数: {}", toQueryString(originalParams), toQueryString(decodedParams));
            }

            return decodedParams;
        }
    }

    /**
     * 将查询参数转换为查询字符串
     *
     * @param queryParams 查询参数
     * @return 查询字符串
     */
    public static String toQueryString(Map<String, String[]> queryParams) {
        return queryParams.entrySet().stream()
            .flatMap(entry -> CollUtil.newArrayList(entry.getValue()).stream()
                .map(value -> entry.getKey() + "=" + value))
            .collect(Collectors.joining("&"));
    }
}
